昨天我們學會了如何從 UI 呼叫 API,把股票清單顯示在 DataGrid 上。
今天要解決一個新問題:離線狀態要怎麼用?
解法是:下載的資料存進 LiteDB,下次啟動時就能先讀取本地資料。
LiteDB 是一個輕量的 NoSQL 本地資料庫,只會生成一個 .db 檔案,非常適合桌面應用程式。
為了保持彈性,我們先定義一個資料存取介面 IStockRepository,負責存股票清單。
// Repositories/IStockRepository.cs
using System.Collections.Generic;
public interface IStockRepository
{
    void SaveStocks(IEnumerable<StockProfile> stocks);
    IEnumerable<StockProfile> LoadStocks();
}
// Repositories/LiteDbStockRepository.cs
using LiteDB;
using System;
using System.Collections.Generic;
public class LiteDbStockRepository : IStockRepository
{
    private readonly string _dbPath;
    public LiteDbStockRepository(string dbPath = "StockData.db")
    {
        _dbPath = dbPath;
    }
    public void SaveStocks(IEnumerable<StockProfile> stocks)
    {
        using var db = new LiteDatabase(_dbPath);
        var col = db.GetCollection<StockProfile>("stocks");
        col.DeleteAll();            // 先清空,避免重複
        col.InsertBulk(stocks);     // 批次寫入
    }
    public IEnumerable<StockProfile> LoadStocks()
    {
        using var db = new LiteDatabase(_dbPath);
        var col = db.GetCollection<StockProfile>("stocks");
        return col.FindAll();
    }
}
LoadStocksCommand:呼叫 API → 存 LiteDB → 更新 UILoadFromLocalCommand:從本地 LiteDB 載入// ViewModels/StockListViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Input;
public class StockListViewModel : INotifyPropertyChanged
{
    private readonly IStockApiService _api;
    private readonly IStockRepository _repo;
    public ObservableCollection<StockProfile> Stocks { get; } = new ObservableCollection<StockProfile>();
    public ICommand LoadStocksCommand { get; }
    public ICommand LoadFromLocalCommand { get; }
    public StockListViewModel(IStockApiService api, IStockRepository repo)
    {
        _api = api;
        _repo = repo;
        LoadStocksCommand = new AsyncCommand(LoadStocksAsync);
        LoadFromLocalCommand = new RelayCommand(_ => LoadFromLocal());
    }
    private async Task LoadStocksAsync()
    {
        var list = await _api.GetStocksAsync();
        Stocks.Clear();
        foreach (var s in list) Stocks.Add(s);
        _repo.SaveStocks(list); // 存入本地資料庫
    }
    private void LoadFromLocal()
    {
        var list = _repo.LoadStocks();
        Stocks.Clear();
        foreach (var s in list) Stocks.Add(s);
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
<StackPanel Orientation="Horizontal" Margin="8" Spacing="8">
    <Button Content="下載股票 (API)"
            Command="{Binding LoadStocksCommand}" Width="150"/>
    <Button Content="載入本地資料"
            Command="{Binding LoadFromLocalCommand}" Width="150"/>
</StackPanel>
<DataGrid ItemsSource="{Binding Stocks}" AutoGenerateColumns="False" Margin="8">
    <DataGrid.Columns>
        <DataGridTextColumn Header="代號" Binding="{Binding Code}" Width="100"/>
        <DataGridTextColumn Header="名稱" Binding="{Binding Name}" Width="200"/>
        <DataGridTextColumn Header="產業" Binding="{Binding Industry}" Width="*"/>
    </DataGrid.Columns>
</DataGrid>
// MainWindow.xaml.cs
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var http = new HttpClient();
        var api = new TwseStockApiService(http);
        var repo = new LiteDbStockRepository("StockData.db");
        this.DataContext = new StockListViewModel(api, repo);
    }
}
StockData.db 載入股票清單今天我們學會了: